/*
 * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
 * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
 * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 *
 * This file is part of the lwIP TCP/IP stack.
 *
 * Author: Huawei Technologies
 *
 */

#ifndef __HLIST_H__
#define __HLIST_H__

#include "list.h"

//#if GAZELLE_TCP_PCB_HASH
struct hlist_node {
    /**
     * @pprev: point the previous node's next pointer
     */
    struct hlist_node *next;
    struct hlist_node **pprev;
};

struct hlist_head {
    struct hlist_node *first;
};

struct hlist_tail {
    struct hlist_node *end;
};

struct hlist_ctl {
    struct hlist_head head;
    struct hlist_tail tail;
};

#define INIT_HLIST_CTRL(ptr) {(ptr)->head.first = NULL; (ptr)->tail.end = NULL;}
#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
#define INIT_HLIST_NODE(ptr) {(ptr)->next = NULL; (ptr)->pprev = NULL;}
#define hlist_entry(ptr, type, member) \
    container_of(ptr, type, member)

/**
 * hlist_for_each_entry    - iterate over list of given type
 * @tpos:    the type * to use as a loop cursor.
 * @pos:    the &struct hlist_node to use as a loop cursor.
 * @head:    the head for your list.
 * @member:    the name of the hlist_node within the struct.
 */
#define hlist_for_each_entry(tpos, pos, head, member) \
    for (pos = (head)->first; \
        pos && ({ tpos = hlist_entry(pos, typeof(*tpos), member); 1; }); \
        pos = (pos)->next)

/**
 * next must be != NULL
 * add n node before next node
 *
 * @n: new node
 * @next: node in the hlist
 */
static inline void hlist_add_before(struct hlist_node *n, struct hlist_node *next)
{
    n->pprev = next->pprev;
    n->next = next;
    next->pprev = &n->next;
    *(n->pprev) = n;
}

static inline int hlist_empty(const struct hlist_head *h)
{
    return !h->first;
}

static inline int hlist_unhashed(const struct hlist_node *h)
{
    return !h->pprev;
}

static inline void hlist_del_init(struct hlist_node *n)
{
    struct hlist_node *next = n->next;
    struct hlist_node **pprev = n->pprev;

    if (pprev == NULL) {
        return;
    }

    *pprev = next;
    if (next != NULL) {
        next->pprev = pprev;
    }

    n->next = NULL;
    n->pprev = NULL;
}

static inline void hlist_ctl_del(struct hlist_ctl *ctl, struct hlist_node *n)
{
    if (ctl->head.first == ctl->tail.end) {
        ctl->head.first = NULL;
        ctl->tail.end = NULL;
        return;
    }

    if (ctl->tail.end == n) {
        ctl->tail.end = (struct hlist_node *)n->pprev;
    }

    hlist_del_init(n);
}

static inline struct hlist_node *hlist_pop_tail(struct hlist_ctl *ctl)
{
    if (hlist_empty(&ctl->head)) {
        return NULL;
    }

    if (ctl->head.first == ctl->tail.end) {
        struct hlist_node *ret = ctl->tail.end;
        ctl->tail.end = NULL;
        ctl->head.first = NULL;
        return ret;
    }

    struct hlist_node *temp = ctl->tail.end;

    struct hlist_node **ptailPrev = ctl->tail.end->pprev;
    *ptailPrev = NULL;

    ctl->tail.end = (struct hlist_node *)ptailPrev;
    temp->pprev = NULL;
    return temp;
}

static inline void hlist_add_after(struct hlist_node *n, struct hlist_node *next)
{
    next->next = n->next;
    n->next = next;
    next->pprev = &n->next;
    if (next->next) {
        next->next->pprev = &next->next;
    }
}

static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
{
    struct hlist_node *first = h->first;

    n->next = first;
    if (first != NULL) {
        first->pprev = &n->next;
    }

    h->first = n;
    n->pprev = &h->first;
}

static inline struct hlist_node *hlist_pop_head(struct hlist_ctl *ctl)
{
    if (hlist_empty(&ctl->head)) {
        return NULL;
    }

    struct hlist_node *temp = ctl->head.first;
    hlist_ctl_del(ctl, temp);
    return temp;
}

static inline void hlist_ctl_add_tail(struct hlist_ctl *ctl, struct hlist_node *node)
{
    if (hlist_empty(&ctl->head)) {
        hlist_add_head(node, &ctl->head);
        ctl->tail.end = ctl->head.first;
        return;
    }

    ctl->tail.end->next = node;

    node->pprev = &(ctl->tail.end->next);
    node->next = NULL;
    ctl->tail.end = node;
}

static inline void hlist_ctl_add_head(struct hlist_node *node, struct hlist_ctl *ctl)
{
    hlist_add_head(node, &ctl->head);
    if (ctl->tail.end == NULL) {
        ctl->tail.end = ctl->head.first;
    }
}

static inline void hlist_ctl_add_before(struct hlist_node *n, struct hlist_node *next, struct hlist_ctl *ctl)
{
    hlist_add_before(n, next);
    if (next == ctl->head.first) {
        ctl->head.first = n;
    }
}

static inline void hlist_ctl_add_after(struct hlist_node *n, struct hlist_node *next, struct hlist_ctl *ctl)
{
    hlist_add_after(n, next);
    if (n == ctl->tail.end) {
        ctl->tail.end = next;
    }
}
//#endif /* GAZELLE_TCP_PCB_HASH */

#endif /* __HLIST_H__ */
